package zip;

import Util.CreateFileUtil;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;

public class LZMATest extends TestParent {
    @Param({"1", "2", "3", "4", "5", "6", "7", "8", "9"})
    public int LEVEL;
    static HashMap<Integer, byte[]> compressFile;
    static HashMap<Integer, byte[]> deCompressFile;
    static {
        compressFile = new HashMap<>();
        deCompressFile = new HashMap<>();
        for (int i = 1; i < 10; i++) {
            compressFile.put(i, CreateFileUtil.getBytesByFile("./test/test_"+i+".json"));
            deCompressFile.put(i,CreateFileUtil.getBytesByFile("./test/test_"+i+".LZMA"));
        }
    }

    @Benchmark
    public byte[] compress() throws IOException {
        byte[] out = Base64.getEncoder().encode(lzma((compressFile.get(LEVEL))));
        CreateFileUtil.bytes2File(out,"./test/","test_"+LEVEL+".LZMA");
        return out;
    }

    @Benchmark
    public byte[] deCompress() throws IOException {
        byte[] out = unlzma(Base64.getDecoder().decode(deCompressFile.get(LEVEL)));
        CreateFileUtil.bytes2File(out,"./test/","test_"+LEVEL+".LZMA.json");
        return out;
    }


    public static byte[] lzma(final byte[] inData) throws IOException {
        final SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();

        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final ByteArrayInputStream in = new ByteArrayInputStream(inData);

        try {
            encoder.SetEndMarkerMode(false);
            encoder.WriteCoderProperties(out);
            final long fileSize = in.available();
            for (int i = 0; i < 8; i++) {
                out.write((int) (fileSize >>> (8 * i)) & 0xFF);
            }
            encoder.Code(in, out, -1, -1, null);

            final byte[] compressed = out.toByteArray();
            return Base64.getEncoder().encode(compressed);
        } finally {
            in.close();
            out.close();
        }
    }

    public static byte[] unlzma(final byte[] compressedStr) throws IOException {

        final SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();

        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final ByteArrayInputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(compressedStr));

        try {
            final int propertiesSize = 5;
            final byte[] properties = new byte[propertiesSize];
            if (in.read(properties, 0, propertiesSize) != propertiesSize) {
                throw new IOException("input .lzma file is too short");
            }
            if (!decoder.SetDecoderProperties(properties)) {
                throw new IOException("Incorrect stream properties");
            }
            long outSize = 0;
            for (int i = 0; i < 8; i++) {
                final int v = in.read();
                if (v < 0) {
                    throw new IOException("Can't read stream size");
                }
                outSize |= ((long) v) << (8 * i);
            }
            if (!decoder.Code(in, out, outSize)) {
                throw new IOException("Error in data stream");
            }

            return out.toByteArray();
        } finally {
            in.close();
            out.close();
        }
    }
}
